package com.jy.rhin.support.parsing;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONPath;
import com.jy.rhin.application.enums.DocumentTypeEnum;
import com.jy.rhin.facade.dto.*;
import org.apache.commons.lang.StringUtils;
import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser;
import org.seimicrawler.xpath.JXDocument;
import org.seimicrawler.xpath.JXNode;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Description: 文档解析
 * @Author: lww
 * @Date: 2022/6/21 8:52
 */
public abstract class BaseDocumentParser {

    private static final String JSON_PATH = "$.";

    static final String HEAD_ID_KEY = "$.recordTarget.patientRole.id";

    static final String HEAD_ID_CODE = "$.root";

    static final String ENTRY_KEY = "$.component.structuredBody.component.section.entry";

    static final String SECTION_ENTRY = "$.section.entry";

    static final String SPOT = ".";

    private static final String COLON = ":";

    static final String HEAD = "head";

    static final String COMPONENT = "component";

    static final String COMPONENT_KEY = "$.component.structuredBody.component";

    static final String SECTION_CODE = "$.section.code.code";

    static final String OBSERVATION_CODE = "$.observation.code.code";

    private static final String CONTAINS_VALUE = "|--";

    private static final String REPLACE_VALUE = "[|\\-\\-]";

    private static final String REPLACE_XSI_TYPE_OLD = ":";

    private static final String REPLACE_XSI_TYPE_NEW = "\\:";

    /**
     * 返回处理的文档类型
     *
     * @return
     */
    public abstract DocumentTypeEnum getDocumentType();

    /**
     * 对应文档类型数据解析
     *
     * @param json
     * @param templateRespList
     * @return
     */
    public abstract List<TemplateRespDto> documentParsing(Object json, List<TemplateRespDto> templateRespList);



    /**
     * JsonPath获取value值
     *
     * @param templateRespList 元素字段list
     * @param object           path的数据对象
     * @param subNum           需要截取的|--数量
     */
    void pathValue(List<TemplateRespDto> templateRespList, Object object, Integer subNum) {
        List<String> keyList = new ArrayList<>();
        for (TemplateRespDto templateRespDto : templateRespList) {
            String element = templateRespDto.getYuanSuMC();
            //是否需要截取
            if (Objects.nonNull(subNum)) {
                if (containsNumber(element) >= subNum) {
                    element = element.substring(subNum * 3);
                } else {
                    element = replaceValue(element);
                }
            }
            String elementKey = null;
            //一级节点默认赋值
            if (containsNumber(element) == 0 || keyList.isEmpty()) {
                elementKey = JSON_PATH + replaceValue(element);
                keyList = new ArrayList<>();
                //子级节点添加父级节点key
            } else if (containsNumber(element) >= keyList.size()) {
                elementKey = JSON_PATH + StringUtils.join(keyList.toArray(), SPOT) + SPOT + replaceValue(element);
                //返回到父级节点时删除原有子级节点key
            } else {
                removeKey(keyList, containsNumber(element));
                elementKey = JSON_PATH + StringUtils.join(keyList.toArray(), SPOT) + SPOT + replaceValue(element);
            }
            keyList.add(replaceValue(element));
            String str = object.toString();
            if (elementKey.contains(COLON)) {
                elementKey = elementKey.replace(REPLACE_XSI_TYPE_OLD, REPLACE_XSI_TYPE_NEW);
            }
            Object value = JSONPath.read(str, elementKey);
            if (value instanceof String || value instanceof Integer || value instanceof Long) {
                templateRespDto.setValue(value.toString());
            }
        }
    }

    /**
     * 计算(|--)数量
     *
     * @param str
     * @return
     */
    int containsNumber(String str) {
        int count = 0;
        while (str.contains(CONTAINS_VALUE)) {
            str = str.substring(str.indexOf(CONTAINS_VALUE) + 1);
            ++count;
        }
        return count;
    }

    /**
     * 替换掉字符串的所有|--
     *
     * @param str
     * @return
     */
    private String replaceValue(String str) {
        return str.replaceAll(REPLACE_VALUE, "");
    }

    /**
     * 根据数量删除list后几位的值
     *
     * @param list
     * @param remove
     * @return
     */
    private void removeKey(List<String> list, int remove) {
        if (list.size() > remove) {
            list.subList(remove, list.size()).clear();
        }
    }

    /**
     * 根据code获取array的指定节点
     *
     * @param name
     * @param code
     * @param array
     * @return
     */
    Object arrayPathObject(String name, String code, JSONArray array) {
        Object jsonObject = new Object();
        for (Object object : array) {
            String value = (String) JSONPath.read(object.toString(), name);
            if (Objects.equals(code, value)) {
                jsonObject = object;
            }
        }
        return jsonObject;
    }

    /**
     * 获取object指定节点下面的array
     *
     * @param name
     * @param object
     * @return
     */
    JSONArray objectPathArray(String name, Object object) {
        return (JSONArray) JSONPath.read(object.toString(), name);
    }

    /**
     * 获取object指定节点下面的object
     *
     * @param name
     * @param object
     * @return
     */
    Object objectPathObject(String name, Object object) {
        return JSONPath.read(object.toString(), name);
    }

    public DocumentDetailV2Dto documentParsingV2(String xml, List<TemplateRespDto> templateRespList) {
        DocumentDetailV2Dto  dto = new DocumentDetailV2Dto();
        Map<String,String> template = templateRespList.stream().collect(Collectors
                .toMap(TemplateRespDto::getYuanSuMC,TemplateRespDto::getShuoMing));
        JXDocument jxDocument = JXDocument.create(Parser.xmlParser().parseInput(xml, ""));
        String url = "//ClinicalDocument";
        JXNode root = jxDocument.selNOne(url);
        dto.setHeadModules(initHead(root,url,template));
        dto.setBodyModules(initBody(root,template));
        return dto;
    }

    public DocumentModule createHDLModule(JXNode root, String rootUrl, Map<String,String> template){
        DocumentModule m = new DocumentModule("文档活动类信息");
        List<String> urls = new ArrayList<>();
        urls.add("/realmCode");
        urls.add("/typeId");
        urls.add("/templateId");
        urls.add("/id");
        urls.add("/code");
        urls.add("/title");
        urls.add("/effectiveTime");
        urls.add("/confidentialityCode");
        urls.add("/languageCode");
        urls.add("/setId");
        urls.add("/versionNumber");
        setNode(root,urls,m,rootUrl,template);
        return m;
    }

    public DocumentModule createCYZModule(JXNode root,String rootUrl,Map<String,String> template){
        DocumentModule m = new DocumentModule("参与者类信息");
        List<String> urls = new ArrayList<>();
        urls.add("/recordTarget");
        urls.add("/author");
        urls.add("/custodian");
        urls.add("/legalAuthenticator");
        setNode(root,urls,m,rootUrl,template);
        return m;
    }

    public DocumentModule createGLHDModule(JXNode root,String rootUrl,Map<String,String> template){
        DocumentModule m = new DocumentModule("关联活动类信息");
        List<String> urls = new ArrayList<>();
        urls.add("/relatedDocument");
        setNode(root,urls,m,rootUrl,template);
        return m;
    }

    public void setNode(JXNode root,List<String> subUrls,DocumentModule m,String url,Map<String,String> template){
        subUrls.forEach(sl ->{
            JXNode j = root.selOne(sl);
            if(j!=null) {
                m.getNodes().add(createNode(j,url,template));
            }
        });
    }


    public DocumentNode createNode(JXNode n, String url, Map<String,String> template){
        DocumentNode node = new DocumentNode();
        Element c = n.asElement();
        String childUrl = url+"/"+c.nodeName();
        JXNode j = new JXNode(c).selOne("/code");
        String codeUrl =getCodeValue(j);
        String shuoMing = template.get(childUrl+codeUrl);
        if(StringUtils.isEmpty(shuoMing)){
            //再向下向下一层
            String codeUrl2 =getChildrenCodeValue(c);
            shuoMing = template.get(childUrl+codeUrl2);
            if(StringUtils.isEmpty(shuoMing) ){
                throw  new RuntimeException("找不到当前节点的说明信息,请在模板中配置:"+childUrl+codeUrl);
            }
            codeUrl = codeUrl2;
        }
        node.getAttrs().add(new DocumentNodeAttr("说明",shuoMing));
        node.setName(c.nodeName());
        //节点属性
        List<Attribute> attrs = c.attributes().asList();
        if(!ObjectUtils.isEmpty(attrs)) {
            attrs.forEach(a->{
                node.getAttrs().add(new DocumentNodeAttr(a.getKey(),a.getValue()));
            });
        }
        //节点的值
        if(c.children().isEmpty() && !StringUtils.isEmpty(c.text())) {
            node.getAttrs().add (new DocumentNodeAttr("值",c.text()));
        }
        //递归加载子节点
        if(!c.children().isEmpty()) {
            for(Element e:c.children()) {
                if("//ClinicalDocument/component/structuredBody/component".equals(childUrl)){
                    node.getNodes().add(createNode(new JXNode(e),childUrl,template));
                }else{
                    node.getNodes().add(createNode(new JXNode(e),childUrl+codeUrl,template));
                }
            }
        }
        return node;

    }

    public List<DocumentModule> initHead(JXNode root, String url, Map<String,String> template){
        List<DocumentModule> list = new ArrayList<>();
        //头部三大模块
        list.add(createHDLModule(root,url,template));
        list.add(createCYZModule(root,url,template));
        list.add(createGLHDModule(root,url,template));
        return list;
    }


    public List<DocumentModule> initBody(JXNode root, Map<String,String> template){
        List<DocumentModule> list = createBodyModule();
        list.forEach(m ->{
            JXNode j = root.selOne(m.getUrl());
            if(j!=null){
                m.getNodes().add(createNode(new JXNode(j.asElement().parent().parent()),"//ClinicalDocument/component/structuredBody",template));
            }
        });
        return list;
    }

    public abstract List<DocumentModule>  createBodyModule();


    private boolean isMatch(String regex,String input){
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(input);
        return matcher.matches();
    }
    public String getCodeValue(JXNode j ){
        if(j!=null){
            Element code = j.asElement();
            String codeValue = code.attr("code");
            if(StringUtils.isEmpty(codeValue)){
                codeValue = code.attr("displayName");
            }

            String regex = "^DE\\d{2}\\.\\d{2}\\.\\d{3}\\.\\d{2}$";
            String regex3 = "^[\\u4e00-\\u9fa5]+$";
            if(  StringUtils.isNotEmpty(codeValue)&& (isMatch(regex,codeValue) ||
                    codeValue.contains("-") || isMatch(regex3,codeValue))){
                return "@code=" + codeValue;
            }
        }
        return "";
    }

    /**
     * 获取子节点下面的code值
     * @param e
     * @return
     */
    public String getChildrenCodeValue(Element e){
        if(!e.children().isEmpty()){
            for(Element c :e.children()){
                //获取下层级code值
                JXNode code = new JXNode(c).selOne("/code");
                String codeValue = getCodeValue(code);
                if (!StringUtils.isEmpty(codeValue)) {
                    return  codeValue;
                }
            }
        }
        return "";
    }


}
